package dataProcess;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;

import type.LPU_SrcType;
import type.LPU_TargetType;
import util.MapUtil;
import util.MathUtil;
import util.ReadSummary;

import ds.Constants;

/**
 * default directory is "output/LPU/"
 * @author make
 * @date 2013/5/20
 */
public class ForLPU {
	
	private  ArrayList<String> posDocs;
	private  ArrayList<String> negaDocs;
	
	private HashSet<String> positiveBugId;
	
	private  ArrayList<String> LPU_pos;
	private  ArrayList<String> LPU_unlabeled;
	private  ArrayList<String> LPU_test;
	
	private HashSet<String> unlabeledIdSet;
		
	public ForLPU(){
		posDocs = new ArrayList<String>();
		negaDocs = new ArrayList<String>();
		LPU_pos = new ArrayList<String>();
		LPU_unlabeled = new ArrayList<String>();
		LPU_test = new ArrayList<String>();
		
		positiveBugId = MapUtil.readPosId(Constants.posIDmap);
		
		unlabeledIdSet = new HashSet<String>();
	}
	
	public ForLPU(HashSet<String> UnlabelBugIdSet){
		posDocs = new ArrayList<String>();
		negaDocs = new ArrayList<String>();
		LPU_pos = new ArrayList<String>();
		LPU_unlabeled = new ArrayList<String>();
		LPU_test = new ArrayList<String>();
		
		positiveBugId = MapUtil.readPosId(Constants.posIDmap);

		unlabeledIdSet = UnlabelBugIdSet;
	}
	
	public ArrayList<String> getPos(){
		return this.LPU_pos;
	}
	
	public ArrayList<String> getUnlabel(){
		return this.LPU_unlabeled;
	}
	
	public ArrayList<String> getTest(){
		return this.LPU_test;
	}
	
	public static void main(String[] args){

	}
	
	public void run(double[] params, String dataSetName){
		//initialize positive & negative
		readTfFile();

		//divide into positive, unlabeled, test
		splitDataForLPU(params[0], LPU_SrcType.positive, LPU_TargetType.LPU_pos);
		splitDataForLPU(params[1], LPU_SrcType.positive, LPU_TargetType.LPU_test);
		splitDataForLPU(params[2], LPU_SrcType.negative, LPU_TargetType.LPU_test);
		this.LPU_unlabeled.addAll(posDocs);
		this.LPU_unlabeled.addAll(negaDocs);
		//write result
		writePos(Constants.LPU_dir + dataSetName);
		writeUnlabel(Constants.LPU_dir + dataSetName);
		writeTest(Constants.LPU_dir + dataSetName);
	}
	
	public void runWithOriginUnlabel(){
		/**
		 * initialize positive & negative
		 */
		readTfFile();
		
		/**
		 * divide into positive, unlabeled, test
		 */	
		//positive set
		splitDataForLPU(0.6, LPU_SrcType.positive, LPU_TargetType.LPU_pos);		
		//unlabeled set
		getOriginalUnlabelDocs();
		//test set
		boolean flag = negaDocs.removeAll(LPU_unlabeled);
		this.LPU_test.addAll(negaDocs);
		this.LPU_test.addAll(posDocs);
		
		/**
		 * write result
		 */
		String dataSetName = "newLPU";
		writePos(Constants.LPU_dir + dataSetName);
		writeUnlabel(Constants.LPU_dir + dataSetName);
		writeTest(Constants.LPU_dir + dataSetName);
	}
	
	public void runWithSameTest(String curLabel, double posRate){
		/**LPU_test, posDocs, negaDocs
		 */
		readTFforSameTest();	

		//LPU_pos
		splitDataForLPU(posRate, LPU_SrcType.positive, LPU_TargetType.LPU_pos);		
		//LPU_unlabel
		this.LPU_unlabeled.addAll(posDocs);
		this.LPU_unlabeled.addAll(negaDocs);
		/**
		 * write result
		 */
		String dataSetName = curLabel;
		writePos(Constants.LPU_dir + dataSetName);
		writeUnlabel(Constants.LPU_dir + dataSetName);
		writeTest(Constants.LPU_dir + dataSetName);
	}
	
	private  void readTfFile(){
		try {
			BufferedReader instream = new BufferedReader(new FileReader(Constants.LPU_TfFile));
			int[] values = ReadSummary.readTfFileSummary(instream.readLine());
			int firstPart = values[1], secondPart = values[2];
			
			while(firstPart > 0){
				posDocs.add(instream.readLine());
				firstPart--;
			}
			while(secondPart > 0){
				negaDocs.add(instream.readLine());
				secondPart--;
			}
			
			instream.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 3 categories have same test data set
	 */
	private void readTFforSameTest(){
		try {
			BufferedReader instream = new BufferedReader(new FileReader(Constants.LPU_TfFile));
			int[] values = ReadSummary.readTfFileSummary(instream.readLine());
			int firstPart = values[1], secondPart = values[2];
			
			while(firstPart > 0){
				String curLine = instream.readLine();		
				String[] pairs = curLine.split(" ");
				String bugID = pairs[0].substring(0, pairs[0].length()-1);
				if(this.positiveBugId.contains(bugID))
					this.posDocs.add(curLine);		
				else 
					this.negaDocs.add(curLine);
				firstPart--;
			}
			
			while(secondPart > 0){
				this.LPU_test.add(instream.readLine());
				secondPart--;
			}
			
			instream.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void splitDataForLPU(double rate, LPU_SrcType srcType, LPU_TargetType targetType){
		ArrayList<String> srcList;
		ArrayList<String> targetList;
		
		if(srcType == LPU_SrcType.positive)
			srcList = this.posDocs;
		else 
			srcList = this.negaDocs;
		
		if(targetType == LPU_TargetType.LPU_pos)
			targetList = this.LPU_pos;
		else if(targetType == LPU_TargetType.LPU_test)
			targetList = this.LPU_test;
		else
			targetList = this.LPU_unlabeled;
		
		HashSet<Integer> posIndexSet = MathUtil.randomNumProducer(
				(int) (rate * srcList.size()), srcList.size());
		Iterator<Integer> pis = posIndexSet.iterator();
		while(pis.hasNext()){
			int curIndex = pis.next();
			targetList.add(srcList.get(curIndex));
		}
		srcList.removeAll(targetList);
	}
	
	private void getOriginalUnlabelDocs(){
		for(String curLine : this.negaDocs){
			String[] pairs = curLine.split(" ");
			String bugID = pairs[0].substring(0, pairs[0].length()-1);
			if(this.unlabeledIdSet.contains(bugID))
				this.LPU_unlabeled.add(curLine);
		}
	}
	
	private  void noTargetWriter(String fileName){
		ArrayList<String> noTargetList;
		if(fileName.endsWith(".pos"))
			noTargetList = this.LPU_pos;
		else
			noTargetList = this.LPU_unlabeled;
		try {
			FileWriter fw = new FileWriter(fileName);
			
			for(String curDoc : noTargetList){
				StringBuilder strBuilder = new StringBuilder();
				String[] TFpairs = curDoc.split(" ");
				
				for(int i = 1; i < TFpairs.length; i++){
					String TFpair = TFpairs[i];
					int wordIndex = Integer.parseInt(TFpair.split(":")[0]);
					wordIndex += 1;
					int termFrequency = (int)Double.parseDouble(TFpair.split(":")[1]);
					if(i != TFpairs.length-1)
						strBuilder.append(wordIndex + ":" + termFrequency + " ");
					else
						strBuilder.append(wordIndex + ":" + termFrequency);
				}
				
				fw.write(strBuilder.toString() + "\n");
			}
			
			fw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private  void writePos(String fileName){
		noTargetWriter(fileName + ".pos");
	}
	
	private  void writeUnlabel(String fileName){
		noTargetWriter(fileName + ".unlabel");
	}
	
	private void writeTest(String fileName){
		fileName = fileName.concat(".test");
		
		try {
			FileWriter fw = new FileWriter(fileName);
			
			for(String curDoc : this.LPU_test){
				StringBuilder strBuilder = new StringBuilder();
				int target = 1;
				String[] TFpairs = curDoc.split(" ");
				
				String bugID = TFpairs[0].substring(0, TFpairs[0].length()-1);
				if(this.positiveBugId.contains(bugID))
					target = 1;
				else
					target = -1;
				strBuilder.append(target + " ");
				
				for(int i = 1; i < TFpairs.length; i++){
					String TFpair = TFpairs[i];
					int wordIndex = Integer.parseInt(TFpair.split(":")[0]);
					wordIndex += 1;
					int termFrequency = (int)Double.parseDouble(TFpair.split(":")[1]);
					if(i != TFpairs.length-1)
						strBuilder.append(wordIndex + ":" + termFrequency + " ");
					else
						strBuilder.append(wordIndex + ":" + termFrequency);
				}
				
				fw.write(strBuilder.toString() + "\n");
			}
			
			fw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}
